package com.gdufe.order.service.impl;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gdufe.common.exception.ServiceException;
import com.gdufe.common.feign.ProductFeign;
import com.gdufe.common.feign.UserFeign;
import com.gdufe.order.dto.CreateOrderDto;

import com.gdufe.common.entity.OrderEntity;
import com.gdufe.order.vo.UserOrderDetilsVo;
import com.gdufe.order.vo.UserOrderVo;
import com.gdufe.common.entity.Product;
import com.gdufe.common.entity.User;


import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gdufe.common.utils.PageUtils;

import com.gdufe.order.dao.OrderDao;
import com.gdufe.order.service.OrderUserService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

@Slf4j
@CacheConfig(cacheNames = "order")
@Service("orderUserService")
public class OrderUserServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderUserService {

    @Resource(type = UserFeign.class)
    private UserFeign userFeign;
    @Resource(type = ProductFeign.class)
    private ProductFeign productFeign;

    @Autowired
    private RedisTemplate redisTemplate;
    public static final String ORDER_KEY = "orderDetails";
    public  static final String SEC_KILL_GOODS_KEY="seckillgoods";

    @Resource(type = RocketMQTemplate.class)
    private RocketMQTemplate rocketMQTemplate;

    private CountDownLatch latch;
    public OrderUserServiceImpl(){

        //创建消费者
        DefaultMQPushConsumer consumer=new DefaultMQPushConsumer("mall-order");
        consumer.setNamesrvAddr("localhost:9876");
        consumer.setMessageModel(MessageModel.BROADCASTING);
        try {
            consumer.subscribe("changstatus","*");
        } catch (MQClientException e) {
            throw new RuntimeException(e);
        }
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext consumeConcurrentlyContext) {

                // 处理消息
                for (MessageExt msg : msgs) {
                    OrderEntity changedOrder = null;
                    // 解析消息内容为对象实例
                    try {
                        String orderData = new String(msg.getBody());
                        changedOrder = new ObjectMapper().readValue(orderData,OrderEntity.class);
                        log.info("收到消息{}",changedOrder);
                    } catch (Exception e) {
                        log.error("反序列化失败");
                        e.printStackTrace();
                    }
                    // 更新数据库
                    updateById(changedOrder);
                    latch.countDown();
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        try {
            consumer.start();
        } catch (MQClientException e) {
            throw new RuntimeException(e);
        }
    }

    //折扣
    public static final BigDecimal discount1 = new BigDecimal("0.9");
    public static final BigDecimal discount2 = new BigDecimal("0.85");
    public static final BigDecimal discount3 = new BigDecimal("0.8");

    //@Cacheable(key = "#params")
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        //获取查询条件
        String key = (String) params.get("key");
        String status = (String) params.get("status");
        String productName = (String)params.get("productName");
        QueryWrapper<OrderEntity> wrapper = new QueryWrapper<>();
        //获取用户信息
        User user = userFeign.getClientUser().getData(new TypeReference<User>() {});
        //获取该用户的所有订单
        wrapper.eq("user_id", user.getId());
        //若用户对订单状态进行筛查，即传入的status非空，则对根据订单状态进行筛选
        if(!StringUtils.isEmpty(status)){
            wrapper.eq("status",status);
        }
        //若用户对订单id或商品id进行筛查，即传入的key非空，则对根据订单信息进行筛选
        if(!StringUtils.isEmpty(key)){
            wrapper.eq("id",key).or().eq("product_id",key);
        }
        List<OrderEntity> orders = this.list(wrapper);
        //转为展示订单
        List<UserOrderVo> vos = new ArrayList<>();
        //如果商品名称为空则获取该用户所有订单，非空则对商品名字进行匹配
        if (StringUtils.isEmpty(productName)) {
            vos = orders.stream().map(item -> {
                UserOrderVo vo = new UserOrderVo();
                Product pro = productFeign.userSelectById(item.getProductId()).getData(new TypeReference<Product>() {
                });
                String proName = pro.getName();
                vo.setProductName(proName);
                BeanUtils.copyProperties(item, vo);

                return vo;
            }).collect(Collectors.toList());
        } else {
            for (OrderEntity item : orders) {
                Product pro = productFeign.userSelectById(item.getProductId()).getData(new TypeReference<Product>() {
                });
                String proName = pro.getName();
                if (proName.equals(productName)) {
                    UserOrderVo orderVo = new UserOrderVo();
                    BeanUtils.copyProperties(item, orderVo);
                    orderVo.setProductName(proName);
                    vos.add(orderVo);
                }
            }
        }

        //进行分页处理
        Integer pageIndex = Integer.parseInt(params.get("page").toString());
        Integer limit = Integer.parseInt(params.get("limit").toString());
        int from = (pageIndex - 1) * limit;
        int to = from + limit > vos.size() ? vos.size() : from + limit;
        List<UserOrderVo> vos1 = new ArrayList<>(vos.subList(from, to));
        IPage<UserOrderVo> page = new Page<>(pageIndex, limit);
        page.setRecords(vos1);
        page.setTotal(vos.size());
        page.setPages(vos.size() % limit > 0 ? vos.size() / limit + 1 : vos.size() / limit);

        return new PageUtils(page);
    }


    //创建订单
    @CacheEvict(allEntries = true)
    @Override
    @Transactional
    public void createOrder(CreateOrderDto orderDto) {

        OrderEntity order = new OrderEntity();
        BeanUtils.copyProperties(orderDto, order);

        //获取用户信息
        User user = userFeign.getClientUser().getData(new TypeReference<User>() {
        });
        int level = user.getLevel();
        int productNum = orderDto.getProductNum();


        //获取商品价格
        BigDecimal price = orderDto.getPrice();

        //计算支付金额
        BigDecimal payAmount = price.multiply(BigDecimal.valueOf(productNum));
        switch (level) {
            case 1:
                payAmount = payAmount.multiply(discount1);
                break;
            case 2:
                payAmount = payAmount.multiply(discount2);
                break;
            case 3:
                payAmount = payAmount.multiply(discount3);
                break;
            default:
                break;
        }
        //生成订单写入数据库
        order.setPayAmount(payAmount);
        Date date = new Date(System.currentTimeMillis());
        order.setUserId(user.getId());
        order.setCreateTime(date);
        order.setStatus(0);
        System.out.println("生成订单："+order);
        baseMapper.insert(order);

        //判断订单是否来源秒杀模块，是则预扣减redis库存
        if(orderDto.getKey()==1) {
            log.info("来源秒杀模块");
            //获取操作redis hash类型的操作类
            BoundHashOperations<String,Object ,Object> hashOperations = redisTemplate.boundHashOps(SEC_KILL_GOODS_KEY);
            Integer num = Integer.valueOf(hashOperations.get(orderDto.getSeckillId().toString()).toString());
            num-=orderDto.getProductNum();

            hashOperations.put(orderDto.getSeckillId().toString(),num.toString());
            //发送消息
            Map<String, Object> map = new HashMap<>();
            map.put("orderId", order.getId());
            map.put("seckill", orderDto);
            Message<Map<String, Object>> message = MessageBuilder.withPayload(map).build();
//            level:(1s/5s/10s/30s/1m/2m/3m/4m/5m/6m/7m/8m/9m/10m/20m/30m/1h/2h)
            rocketMQTemplate.syncSend("seckillorder", message, 10000, 5);
        }

    }

    //用户获取订单详情
    @Override
    public UserOrderDetilsVo getOrderDetils(Long id) {
        //获取操作redis hash类型的操作类
        BoundHashOperations<String, Object, Object> hashOperations = redisTemplate.boundHashOps(ORDER_KEY);
        //若缓存中有就用缓存的
        if (hashOperations.hasKey(id.toString())) {
            return (UserOrderDetilsVo) hashOperations.get(id.toString());
        }

        //获取数据库订单信息
        OrderEntity order = this.getById(id);
        if (order == null) {
            throw new ServiceException(500, "查无此单");
        }
        UserOrderDetilsVo vo = new UserOrderDetilsVo();
        BeanUtils.copyProperties(order, vo);
        //获取商品价格和商品名，组成订单详情
        Product product = productFeign.userSelectById(order.getProductId()).getData(new TypeReference<Product>() {
        });
        vo.setProductPrice(product.getPrice());
        vo.setProductName(product.getName());

        //缓存该订单详情
//        redisTemplate.expire(ORDER_KEY.toString(),80, TimeUnit.SECONDS);
//        redisTemplate.expire(id.toString(),30, TimeUnit.SECONDS);

        hashOperations.put(id.toString(), vo);
        hashOperations.expire(80, TimeUnit.SECONDS);
        return vo;
    }

    //用户支付订单
    @Caching(evict = {
            @CacheEvict(value = "order", allEntries = true),
            @CacheEvict(value = "product", allEntries = true)
    })
    @Override
    public boolean payOrder(Long id) {
        latch = new CountDownLatch(1);
        boolean result = false;
        //获取操作redis hash类型的操作类
        BoundHashOperations<String, Object, Object> hashOperations = redisTemplate.boundHashOps(ORDER_KEY);

        //根据id获取订单实体
        OrderEntity order = this.getById(id);

        //包装成消息发送给product模块
        Message<OrderEntity> message = MessageBuilder.withPayload(order).build();
        rocketMQTemplate.syncSend("payorder",  message);
        log.info("发送消息");

        //等待接收
        try {
            boolean pay=latch.await(20,TimeUnit.SECONDS);
            if(pay) System.out.println("await true");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        OrderEntity changedOrder=this.getById(id);
        if(changedOrder.getStatus()==1)
            result=true;
        else if(changedOrder.getStatus()==2)
            result=false;
        else System.out.println("未支付");


//        Product product = productFeign.userSelectById(order.getProductId()).getData(new TypeReference<Product>() {
//        });

//        //查询商品库存是否足够
//        if (product.getStock() >= order.getProductNum()) {
//            int points = product.getPayPoint();
//            //修改订单状态与设置支付时间
//            order.setStatus(1);
//            order.setPayTime(new Date());
//            //扣减商品库存
//            ReduceStockVo vo = new ReduceStockVo(order.getProductId(), order.getProductNum());
//            productFeign.reduceStock(vo);
//            //给用户增加积分
//            AddPointsVo addPointsVo = new AddPointsVo(order.getUserId(), points * order.getProductNum());
//            userFeign.addPoints(addPointsVo);
//            this.updateById(order);
//            result = true;
//        } else {
//            //库存不足，修改订单状态
//            order.setStatus(2);
//            order.setPayResoult(1);
//            this.updateById(order);
//            result = false;
//        }

        //订单信息更新到缓存
        UserOrderDetilsVo vo;
        if (hashOperations.hasKey(id.toString())) {
            vo = (UserOrderDetilsVo) hashOperations.get(id.toString());
            BeanUtils.copyProperties(changedOrder, vo);
            hashOperations.put(vo.getId().toString(), vo);
        }

        return result;
    }

    //用户取消订单，修改订单状态
    @CacheEvict(allEntries = true)
    @Override
    public void cancelOrder(Long id) {
        //获取操作redis hash类型的操作类
        BoundHashOperations<String, Object, Object> hashOperations = redisTemplate.boundHashOps(ORDER_KEY);

        //业务逻辑
        OrderEntity order = this.getById(id);
        order.setStatus(2);
        order.setPayResoult(0);
        this.updateById(order);

        //订单信息更新到缓存
        UserOrderDetilsVo vo;
        if (hashOperations.hasKey(id.toString())) {
            vo = (UserOrderDetilsVo) hashOperations.get(id.toString());
            BeanUtils.copyProperties(order, vo);
            hashOperations.put(vo.getId().toString(), vo);
        }
    }



    //删除缓存
    @CacheEvict(allEntries = true)
    @Override
    public boolean removeByIds(Collection<?> list) {
        //获取操作redis hash类型的操作类
        BoundHashOperations<String, Object, Object> hashOperations = redisTemplate.boundHashOps(ORDER_KEY);
        //删除id对应的缓存
        for (Object id : list) {
            if (hashOperations.hasKey(((Long) id).toString())) {
                hashOperations.delete(((Long) id).toString());
            }
        }
        return super.removeByIds(list);
    }
}